springcloud gateway配置RedisRateLimiter限流无效但无报错信息,redis中无限流key

您所在的位置:网站首页 springcloud gateway限流配置 springcloud gateway配置RedisRateLimiter限流无效但无报错信息,redis中无限流key

springcloud gateway配置RedisRateLimiter限流无效但无报错信息,redis中无限流key

2023-06-28 14:05| 来源: 网络整理| 查看: 265

最近在学习springcloud gateway在学习限流时配置使用的RedisRateLimiter配置了限流,但是redis中无法存入key并且限流无效,但是后台却无报错信息,我使用的是springcloud版本是2.1.1.RELEASE

springcloud gateway限流教程 https://blog.csdn.net/forezp/article/details/85081162

分析源码控制限流的代码主要在RedisRateLimiter中的isAllowed方法

@Override @SuppressWarnings("unchecked") //根据keyresolver对request生成1个routeId,然后对该routeId采取其对应的限流 public Mono isAllowed(String routeId, String id) { if (!this.initialized.get()) { throw new IllegalStateException("RedisRateLimiter is not initialized"); } Config routeConfig = loadConfiguration(routeId); // How many requests per second do you want a user to be allowed to do?1秒多少个请求 int replenishRate = routeConfig.getReplenishRate(); // How much bursting do you want to allow?令牌桶的总容量 int burstCapacity = routeConfig.getBurstCapacity(); try { //redis中存有上次请求的时间,上次桶中剩下的令牌数两个key List keys = getKeys(id); // The arguments to the LUA script. time() returns unixtime in seconds. //执行脚本request_rate_limiter.lua(在gateway-core包中),请求中有四个参数 生成令牌速率,桶容量,当前时间,请求令牌数 List scriptArgs = Arrays.asList(replenishRate + "", burstCapacity + "", Instant.now().getEpochSecond() + "", "1"); // allowed, tokens_left = redis.eval(SCRIPT, keys, args) Flux flux = this.redisTemplate.execute(this.script, keys, scriptArgs); // .log("redisratelimiter", Level.FINER); return flux.onErrorResume(throwable -> Flux.just(Arrays.asList(1L, -1L))) .reduce(new ArrayList(), (longs, l) -> { longs.addAll(l); return longs; }).map(results -> { boolean allowed = results.get(0) == 1L; Long tokensLeft = results.get(1); Response response = new Response(allowed, getHeaders(routeConfig, tokensLeft)); if (log.isDebugEnabled()) { log.debug("response: " + response); } return response; }); } catch (Exception e) { /* * We don't want a hard dependency on Redis to allow traffic. Make sure to set * an alert so you know if this is happening too much. Stripe's observed * failure rate is 0.01%. */ log.error("Error determining if user allowed from redis", e); } return Mono.just(new Response(true, getHeaders(routeConfig, -1L))); }

主要问题就是在蓝色代码这一段在flux发生异常时,他的异常处理是直接使用flux([1,-1])这样一个数组进行后续操作,居然没有异常日志输出,导致一直请求redis失败,但是无异常日志,并且throwable -> Flux.just(Arrays.asList(1L, -1L)是在行内完成,无法直接定位异常信息,只能一层层往下找,在execute中有个eval方法

protected Flux eval(ReactiveRedisConnection connection, RedisScript script, ReturnType returnType, int numKeys, ByteBuffer[] keysAndArgs, RedisElementReader resultReader) { Flux result = connection.scriptingCommands().evalSha(script.getSha1(), returnType, numKeys, keysAndArgs); result = result.onErrorResume(e -> { if (ScriptUtils.exceptionContainsNoScriptError(e)) { return connection.scriptingCommands().eval(scriptBytes(script), returnType, numKeys, keysAndArgs); } return Flux .error(e instanceof RuntimeException ? (RuntimeException) e : new RedisSystemException(e.getMessage(), e)); }); return script.returnsRawValue() ? result : deserializeResult(resultReader, result); }

将断点打在蓝色代码处终于定位到异常信息

org.springframework.data.redis.RedisSystemException: Error in execution; nested exception is io.lettuce.core.RedisCommandExecutionException: ERR unknown command 'EVALSHA'

意思是redis不支持evalsha命令,估计是redis版本过低,不支持此命令,然后在虚拟机上重装了最新的5.0.5版本redis开墙等操作后,限流终于生效

附request_rate_limiter.lua注释后文件 --指定三个参数tokens_key,timestamp_key local tokens_key = KEYS[1] local timestamp_key = KEYS[2] --redis.log(redis.LOG_WARNING, "tokens_key " .. tokens_key) --获取rate,capacity,now,requested四个参数并转成数字 local rate = tonumber(ARGV[1]) local capacity = tonumber(ARGV[2]) local now = tonumber(ARGV[3]) local requested = tonumber(ARGV[4]) --装载时间 = capacity/rate local fill_time = capacity/rate --floor保留整数位 local ttl = math.floor(fill_time*2) --redis.log(redis.LOG_WARNING, "rate " .. ARGV[1]) --redis.log(redis.LOG_WARNING, "capacity " .. ARGV[2]) --redis.log(redis.LOG_WARNING, "now " .. ARGV[3]) --redis.log(redis.LOG_WARNING, "requested " .. ARGV[4]) --redis.log(redis.LOG_WARNING, "filltime " .. fill_time) --redis.log(redis.LOG_WARNING, "ttl " .. ttl) --如果last_tokens== nil(不存在) local last_tokens = tonumber(redis.call("get", tokens_key)) if last_tokens == nil then last_tokens = capacity end --redis.log(redis.LOG_WARNING, "last_tokens " .. last_tokens) local last_refreshed = tonumber(redis.call("get", timestamp_key)) if last_refreshed == nil then last_refreshed = 0 end --redis.log(redis.LOG_WARNING, "last_refreshed " .. last_refreshed) --请求距离上次请求过的了几秒 local delta = math.max(0, now-last_refreshed) --令牌池中的请求还剩多少 local filled_tokens = math.min(capacity, last_tokens+(delta*rate)) --令牌池中的请求大于请求数则允许请求 local allowed = filled_tokens >= requested --如果allow则生成新的令牌数 new_tokens且allow_num为1 local new_tokens = filled_tokens local allowed_num = 0 if allowed then new_tokens = filled_tokens - requested allowed_num = 1 end --redis.log(redis.LOG_WARNING, "delta " .. delta) --redis.log(redis.LOG_WARNING, "filled_tokens " .. filled_tokens) --redis.log(redis.LOG_WARNING, "allowed_num " .. allowed_num) --redis.log(redis.LOG_WARNING, "new_tokens " .. new_tokens) --缓存请求后令牌数和当期时间 redis.call("setex", tokens_key, ttl, new_tokens) redis.call("setex", timestamp_key, ttl, now) --返回获取是否允许和令牌数 return { allowed_num, new_tokens }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3